﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace COMP476_A3
{
    enum Behavior_Movement
    {
        Kinematic,
        Dynamic
    }
    enum Behavior_AI
    {
        Seek,
        Flee,
        Wander,
    }
    class Mover : GameObject
    {
        protected const float minSpeed = 1;
        protected const float maxSpeed = 5;
        protected const float maxAccel = 10;
        protected const float maxRotation = 5;

        protected Vector3 linear;
        protected float angular;

        protected Vector3 velocity;

        protected float orientation;
        protected float rotation;

        protected TimeSpan timestep;

        protected Vector3 targetReference;
        public Vector3 targetDirection;
        public float rayLength = 2;

        protected Random random;

        protected float timeBetweenUpdate = 0.1f;
        protected float currentElapsedTime;

        protected int currentDirection;

        protected float angleModifier = 32;
        protected float directionLeftThreshold = -16;
        protected float directionRightThreshold = 16;

        public Ray LeftRay;
        public Ray RightRay;

        public bool alteringCourse = false;

        public Behavior_Movement BH_Movement { get; set; }
        public Behavior_AI BH_AI { get; set; }

        public Mover(Model _model) : base(_model) { Init();  }

        private void Init()
        {
            //Initializes values

            velocity = new Vector3(0, 0, 0);

            orientation = 0;
            rotation = 0;

            linear = new Vector3(0, 0, 0);
            angular = 0;

            timestep = TimeSpan.Zero;

            currentElapsedTime = timeBetweenUpdate;

            currentDirection = 0;

            targetReference = new Vector3(0, 0, rayLength);
            targetDirection = new Vector3(0, 0, rayLength);

            BH_Movement = Behavior_Movement.Dynamic;
            BH_AI = Behavior_AI.Wander;

            LeftRay = new Ray();
            RightRay = new Ray();

            random = new Random((int)DateTime.Now.Ticks);
        }

        public void Update(GameTime gameTime)
        {
            float time = (float)(gameTime.ElapsedGameTime.TotalSeconds - timestep.TotalSeconds);

            velocity += linear * time;
            orientation += angular * time;

            if (velocity.Length() > maxSpeed)
            {
                velocity.Normalize();
                velocity *= maxSpeed;
            }

            Position += velocity * time;
            orientation += rotation * time;
            Yaw = orientation; //maybe wrong O.O

            currentElapsedTime += time;

            if (model != null)
            {
                float width = model.Meshes[0].BoundingSphere.Radius * scale;
                Vector3 transformedRaySideReference = Vector3.Transform(new Vector3(width, 0, 0), Matrix.CreateRotationY(Yaw));
                Vector3 transformedRayForwardReference = Vector3.Transform(targetReference * scale, Matrix.CreateRotationY(Yaw));

                LeftRay.Position = Position + transformedRaySideReference;
                RightRay.Position = Position - transformedRaySideReference;

                LeftRay.Direction = transformedRayForwardReference;
                RightRay.Direction = transformedRayForwardReference;
            }

            if (BH_AI == Behavior_AI.Wander && currentElapsedTime >= timeBetweenUpdate)
            {
                float modifier = ((float)Math.PI / angleModifier);
                int direction = random.Next(-3, 4);

                currentDirection += direction;

                currentDirection = (int)MathHelper.Clamp(currentDirection, directionLeftThreshold, directionRightThreshold);

                Vector3 targetTransformedReference = Vector3.Transform(targetReference * Scale, Matrix.CreateRotationY(Yaw + (modifier * currentDirection)));
                targetDirection = targetTransformedReference + Position;

                currentElapsedTime = 0;
            }
        }

        public void getSteering(Vector3 Target)
        {
            switch (BH_Movement)
            {
                case Behavior_Movement.Kinematic:
                    getKinematicSteering(Target);
                    break;
                case Behavior_Movement.Dynamic:
                    getDynamicSteering(Target);
                    break;
            }
        }

        public void getKinematicSteering(Vector3 Target)
        {
            velocity = Target - Position;
            if (velocity != Vector3.Zero)
                velocity.Normalize();
            velocity *= maxSpeed;

            orientation = getNewKinematicOrientation();

            rotation = 0;
        }

        public float getNewKinematicOrientation()
        {
            if (velocity.Length() > 0)
                return (float)Math.Atan2((double)velocity.X, (double)velocity.Z);

            return orientation;
        }

        public void getDynamicSteering(Vector3 Target)
        {
            linear = Target - Position;
            if (linear != Vector3.Zero)
                linear.Normalize();
            linear *= maxAccel;

            angular = 0;

            orientation = getNewKinematicOrientation();
        }

        public void AdjustDirectionClamp(float adjust)
        {
            if (adjust < 0 && !alteringCourse) //Adjust to the right
            {
                directionLeftThreshold = (-angleModifier / 4);
                directionRightThreshold = directionLeftThreshold;
                alteringCourse = true;
            }
            else if (adjust > 0 && !alteringCourse) //Adjust to the left
            {
                directionRightThreshold = (angleModifier / 4);
                directionLeftThreshold = directionRightThreshold;
                alteringCourse = true;
            }
            else if (adjust == 0 && alteringCourse)
            {
                alteringCourse = false;
                directionLeftThreshold = (-angleModifier / 4);
                directionRightThreshold = (angleModifier / 4);
            }

            if (alteringCourse && adjust <= rayLength)
                velocity *= (Math.Abs(adjust / rayLength) - (model.Meshes[0].BoundingSphere.Radius * scale));
        }
    }
}
